package ru.niir.goodfil.db.handlers;

import ru.niir.goodfil.db.Good;
import ru.niir.goodfil.db.GoodType;
import ru.niir.goodfil.db.OrderItem;
import ru.niir.goodfil.db.PersonalPrice;
import ru.niir.goodfil.exceptions.InvalidIdException;
import ru.niir.goodfil.exceptions.WrongHandlerException;
import ru.niir.goodfil.utils.validation.RemoveCheckResult;
import ru.niir.goodfil.utils.validation.ValidationError;
import ru.niir.goodfil.utils.validation.ValidationProblem;
import ru.niir.goodfil.utils.validation.ValidationResult;

import static org.apache.commons.lang.StringUtils.isBlank;

/**
 * Обработчик для товаров
 * @see ru.niir.goodfil.db.Good товары
 */
public class GoodHandler extends Handler
{
    /**
     * Конструктор
     */
    public GoodHandler(Goodfil gf)
    {
        super(Good.class, gf);
    }

    /**
     * Проверки для товаров
     * 1. goodTypeId указан верно (FK)
     * 2. code заполнен
     * 3. code уникален (IDX)
     * 4. с1code либо не заполнен, либо уникален (IDX)
     * 5. name заполнено
     * 6. minPrice > 0
     * 7. weight >= 0 (UNSIGNED)
     * 8. volume >= 0 (UNSIGNED)
     * 9. count >=0 (UNSIGNED)
     * 10. packageCount > 0 (UNSIGNED)   - изменено, теперь текстовая информация, не требующая валидации
     * @param o товар
     * @return результат проверки
     */
    public ValidationResult validate(Object o)
    {
        assert o != null;
        if (!(o instanceof Good)) throw new WrongHandlerException(klass, o.getClass());

        Good good = (Good) o;
        ValidationResult result = ValidationResult.Ok(Good.class);

        if (!exists(GoodType.class, good.getGoodTypeId()))
            result.addValidationError(new ValidationError("goodTypeId", "Группа товаров", good.getGoodTypeId(), ValidationProblem.REFERENCE));

        if (isBlank(good.getCode()))
            result.addValidationError(new ValidationError("code", "Код", good.getCode(), ValidationProblem.NOT_EMPTY));

        if (!checkGoodCodeUnique(good.getCode(), good.getId()))
            result.addValidationError(new ValidationError("code", "Код", good.getCode(), ValidationProblem.UNIQUE));

        if (!isBlank(good.getC1code()) && !checkGoodC1CodeUnique(good.getC1code(), good.getId()))
            result.addValidationError(new ValidationError("c1code", "Код в 1С", good.getC1code(), ValidationProblem.UNIQUE));

        if (isBlank(good.getName()))
            result.addValidationError(new ValidationError("name", "Наименование", good.getName(), ValidationProblem.NOT_EMPTY));

        if (good.getMinPrice() <= 0)
            result.addValidationError(new ValidationError("minPrice", "Мин. цена", good.getMinPrice(), ValidationProblem.POSITIVE_ZERO));

        if (good.getWeight() < 0)
            result.addValidationError(new ValidationError("weight", "Вес", good.getWeight(), ValidationProblem.POSITIVE));

        if (good.getVolume() < 0)
            result.addValidationError(new ValidationError("volume", "Объем", good.getVolume(), ValidationProblem.POSITIVE));

        if (good.getCount() < 0)
            result.addValidationError(new ValidationError("count", "Кол-во на складе", good.getCount(), ValidationProblem.POSITIVE));

      //  if (good.getPackageCount() <= 0)
       //     result.addValidationError(new ValidationError("packageCount", "Кол-во в групповой таре", good.getPackageCount(), ValidationProblem.POSITIVE_ZERO));

        return result;
    }

    @Override
    public void postSave(Object o)
    {
        assert o != null;
        if (!(o instanceof Good)) throw new WrongHandlerException(klass, o.getClass());

        //  Это теперь обабатывается на стороне БД за счет триггера PostSaveGoodTrigger
//        Good good = (Good) o;
//        getSession().createQuery("delete from SimplePricelist where goodId=?").setLong(0, good.getId()).executeUpdate();
//        for (ClientGroup clientGroup : gf.getClientGroups()) getSession().insert(new SimplePricelist(good.getId(), clientGroup.getId(), 0));
    }

    @Override
    public void postRemove(long id)
    {
        //  Это теперь обрабатывается на стороне БД за счет ключей на таблицу SimplePricelist
//        getSession().createQuery("delete from SimplePricelist where goodId=?").setLong(0, id).executeUpdate();
    }

    /**
     * Проверить, возможно ли удалить товар с заданным идентификатором.
     * Товар можно удалить если:
     * 1. С ним не связаны позиции заказов
     * 2. С ним не связаны персональные цены
     */
    public RemoveCheckResult removeCheck(long goodId) throws InvalidIdException
    {
        if (!exists(Good.class, goodId)) throw new InvalidIdException(Good.class, goodId);

        if (0 < (Long)getSession().createQuery("select count(*) from OrderItem where goodId=?").setLong(0, goodId).uniqueResult())
            return RemoveCheckResult.Error(Good.class, OrderItem.class);

        if (0 < (Long)getSession().createQuery("select count(*) from PersonalPrice where goodId=?").setLong(0, goodId).uniqueResult())
            return RemoveCheckResult.Error(Good.class, PersonalPrice.class);

        return RemoveCheckResult.Ok(Good.class);
    }

    /**
     * Проверить, что данный кода товара уникален, КРОМЕ товара с заданным удентификатором.
     * @param goodCode код товара
     * @param id идентификатор товара
     * @return true, если данный кода товара уникален, КРОМЕ товара с заданным удентификатором.
     */
    private boolean checkGoodCodeUnique(String goodCode, long id)
    {
        assert !isBlank(goodCode);

        synchronized (this)
        {
            return 0 == (Long)getSession().createQuery("select count(*) from Good where code = ? and id != ?")
                    .setString(0, goodCode)
                    .setLong(1, id)
                    .uniqueResult();
        }
    }

    /**
     * Проверить, что данный кода товара в 1С уникален, КРОМЕ товара с заданным удентификатором.
     * @param goodCode код товара
     * @param id идентификатор товара
     * @return true, если данный кода товара в 1С уникален, КРОМЕ товара с заданным удентификатором.
     */
    private boolean checkGoodC1CodeUnique(String goodCode, long id)
    {
        assert goodCode != null;

        synchronized (this)
        {
            return 0 == (Long)getSession().createQuery("select count(*) from Good where c1code = ? and id != ?")
                    .setString(0, goodCode)
                    .setLong(1, id)
                    .uniqueResult();
        }
    }
}
